package service

import (
	"context"

	"task/driver"
	"task/ecode"
	"task/models"
	"task/utils"

	log "github.com/sirupsen/logrus"
)

var ExecutorHandlerMap = make(map[string]ExecutorHandler)

// ExecutorHandler executor handler.
// 执行处理器 接口
type ExecutorHandler interface {
	Execute(context.Context, *models.ProcessEvent) error
	Callback(context.Context, *models.ProcessEvent) error
}

// InitExecutorHandler init executor handler.
// 初始化执行处理器
func InitExecutorHandler() {
	ExecutorHandlerMap["process_init"] = InitProcessor{}
	ExecutorHandlerMap["process_grpc"] = GrpcProcessor{}
	ExecutorHandlerMap["process_databus1"] = Biz1Processor{}
	ExecutorHandlerMap["process_databus2"] = Biz2Processor{}
	ExecutorHandlerMap["process_end"] = EndProcessor{}
}

// getExecutorHandler get executor handler.
func getExecutorHandler(name string) (ExecutorHandler, error) {
	var (
		hdl ExecutorHandler
		ok  bool
	)
	if hdl, ok = ExecutorHandlerMap[name]; !ok {
		log.Errorf("Process handler(%s) not found", name)
		return nil, ecode.ExecutorHdlNotFound
	}
	return hdl, nil
}

// processExecuteEvent process execute event.
func processExecuteEvent(ctx context.Context, event *models.ProcessEvent) error {
	log.Infof("Process start with resource(%d) processor(%d)", event.ResourceId, event.ProcessorId)
	var (
		ok  bool
		hdl ExecutorHandler
		err error
	)
	// 检查处理单元
	if event.Processor, ok = ProcessorMap[event.ProcessorId]; !ok {
		return ecode.ProcessorNotFound
	}
	// 处理准备
	if err = PrepareProcess(ctx, event); err != nil {
		return err
	}
	// 获取处理器
	if hdl, err = getExecutorHandler(event.Processor.Handler); err != nil {
		return err
	}
	// 处理执行或回调
	if event.ProcessType == models.ProcessTypeExecute {
		if err = hdl.Execute(ctx, event); err != nil {
			return err
		}
	}
	if event.ProcessType == models.ProcessTypeCallback {
		if err = hdl.Callback(ctx, event); err != nil {
			return err
		}
	}
	// 处理结束
	if err = AfterProcess(ctx, event); err != nil {
		return err
	}
	return nil
}

// handleProcessError handle process error.
func handleProcessError(event *models.ProcessEvent, err error) {
	// 更新处理状态 处理错误
	event.ProcessState = models.ProcessStateError
	event.ProcessMsg = err.Error()
	if event.ResourceProcessState != nil {
		_ = driver.UpdateResourceProcessState(event.ResourceProcessState.Id, event.ProcessState, event.ProcessCnt, event.ProcessMsg)
	} else {
		_ = driver.AddResourceProcessState(event.ResourceId, event.ProcessorId, event.ProcessState, event.ProcessCnt, event.ProcessMsg)
	}
	return
}

func PrepareProcess(_ context.Context, event *models.ProcessEvent) error {
	var err error
	// 检查资源
	if event.Resource, err = driver.GetResource(event.ResourceId); err != nil {
		return err
	}
	if event.Resource == nil {
		return ecode.ResourceNotFound
	}
	// 检查处理状态
	if event.ResourceProcessState, err = driver.GetResourceProcessState(event.ResourceId, event.ProcessorId); err != nil {
		return err
	}
	if event.ResourceProcessState == nil {
		return ecode.ProcessStateNotFound
	}
	event.ProcessState = event.ResourceProcessState.ProcessState
	event.ProcessCnt = event.ResourceProcessState.ProcessCnt
	// 检查处理状态是否等于ready
	if event.ProcessState != models.ProcessStateReady {
		log.Errorf("Resource(%d) processor(%d) state(%d) is not ready", event.ResourceId, event.ProcessorId, event.ProcessState)
		return ecode.ProcessStateWrong
	}
	// 更新处理状态 处理中 处理次数+1(回调不需要)
	if event.ProcessType == models.ProcessTypeExecute {
		event.ProcessCnt += 1
	}
	event.ProcessState = models.ProcessStateRunning
	if err = driver.UpdateResourceProcessState(event.ResourceProcessState.Id, event.ProcessState, event.ProcessCnt, ""); err != nil {
		return err
	}
	return nil
}

func AfterProcess(ctx context.Context, event *models.ProcessEvent) error {
	var err error
	// 更新处理结果
	if err = driver.UpdateResourceProcessState(event.ResourceProcessState.Id, event.ProcessState, event.ProcessCnt, event.ProcessMsg); err != nil {
		return err
	}
	// 当处理结果为success和fail时 发起任务调度
	if utils.IntInSlice(event.ProcessState, []int{models.ProcessStateSuccess, models.ProcessStateFail}) {
		// 进入调度消息队列 等待调度
		if err = driver.SendDispatchEventMsg(ctx, &models.DispatchEvent{
			Event:       models.DispatchEventProcessorDone,
			ResourceId:  event.ResourceId,
			DagId:       event.Resource.DagId,
			ProcessorId: event.ProcessorId,
		}); err != nil {
			return err
		}
	}
	return nil
}
